unit UCliSer;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, System.IniFiles,ShellApi,Data.FmtBcd,
  CPort;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Timer1: TTimer;
    Button4: TButton;
    ComPort1: TComPort;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    Procedure fCheckCommand();
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  ComFile: THandle;
  iPort : Integer;
  iBaudrate : Integer;
  iMode : Integer;
  bArrDataIn : Array[0..20] of Array [0..20] of Byte;
  bArrDataOut : Array[0..20] of Array [0..20] of Byte;
  bCountDataOut : Array[0..20] of Integer;
  iGNumOfDataIn : integer;
  bSerStatus : byte;
  bActiveSerialBuff : Array[0..100] of Byte;
  iGbCounterBuff : integer = 0;  //Counter data checked
  iGbLengthOfEvent : integer; //Total hci event length
  iGCountBase : integer; //Counter for base command
  iActiveSerialBuffLen : Integer;

  implementation

{$R *.dfm}

function ParseDelimitedString(AString, ADelimiter: String;
                              ASectionNumber: Integer;
                              AIncludeDelimiter: Boolean = False;
                              ATrimResult: Boolean = False): String;
var
  i, iCount : integer;
  sValue : String;
  sNextChar: char;
begin
  Result := '';
  iCount := 1;
  for i:= 1 to Length(AString) do
  begin
    sValue := AString[i];
    if (AIncludeDelimiter) and (ASectionNumber > 1)  then
      sNextChar := AString[i+1]
    else
      sNextChar := AString[i];
    if sNextChar = ADelimiter then
      iCount := iCount + 1
    else if iCount = ASectionNumber then
      Result := Result + sValue;
  end;
  if ATrimResult then
    Result := Trim(Result);
end;
function ParseAfterDelimitedString(AString, ADelimiter: String;
                              ASectionNumber: Integer;
                              AIncludeDelimiter: Boolean = False;
                              ATrimResult: Boolean = False): String;
var
  i, iCount : integer;
  sValue : String;
  boSign: boolean;
begin
  Result := '';
  iCount := 1;
  boSign := False;
  for i:= 1 to Length(AString) do
  begin
    sValue := AString[i];
    if(boSign = True) then
        Result := Result + sValue;
    if(sValue = ADelimiter)  then
        boSign := True;
  end;
  Result := Trim(Result);
end;

function ConvertByteToStr (bArrDataInputSerial :  Array of Byte;
                            BytesRead : integer) : String;
var
    sArrActiveBuf : String;
    i : integer;
    cTemp : byte;
begin
    for i := 0 to BytesRead do
    begin
        //Convert first BCD digit
        cTemp := bArrDataInputSerial[i];
        cTemp := cTemp shr 4;
        if(cTemp < $A) then
            cTemp := cTemp + $30
        else
            cTemp := cTemp + $37;
        sArrActiveBuf := sArrActiveBuf + char(cTemp);
        //Convert second BCD digit
        cTemp := bArrDataInputSerial[i];
        cTemp := cTemp AND $0F;
        if(cTemp < $A) then
            cTemp := cTemp + $30
        else
            cTemp := cTemp + $37;
        sArrActiveBuf := sArrActiveBuf + char(cTemp);
    end;
    Result :=  sArrActiveBuf;
end;

procedure OpenIniFile;
var
  SettingsFile : TCustomIniFile;
  sData : array[0..20] of String;
  sMode : String;
  sTemp : String;
  sTemp2:string;
  i : integer;
  i2 : integer;
  i3 : integer;
  i4 : integer;
begin
  { Open an instance }
  SettingsFile := TIniFile.Create(ChangeFileExt(Application.ExeName, '.INI'));

  try
    {
    Read all saved values from the last session. The section name
    is the name of the form. Also use form's properties as defaults
    }
    iPort  := SettingsFile.ReadInteger('Setting', 'Port', 0 );
    iBaudrate  := SettingsFile.ReadInteger('Setting', 'Baudrate', 0 );
    sMode := SettingsFile.ReadString('Data','Mode','NONE');
    if sMode = 'Bluetooth' then
        iMode := 1;

    i:=0;
    i2 := 0;
    repeat
    begin
        sData[i] := SettingsFile.ReadString('Data','Data'+inttostr(i),'kosong');
        if(sData[i] <> 'kosong') then
        begin
            sTemp := ParseDelimitedString(sData[i],':',1);
            sTemp2 := ParseAfterDelimitedString(sData[i],':',1);
            if((sTemp <> '') AND (sTemp2 <> '')) then
            begin
                i3 := 1;
                i4 := 0;
                while (i3 < Length(sTemp)) do
                begin
                    //Convert Input Data
                    if(sTemp[i3] <= '9') then
                        bArrDataIn[i][i4] := (byte(sTemp[i3]) - $30)
                    else
                        bArrDataIn[i][i4] := (byte(sTemp[i3]) - $37);

                    bArrDataIn[i][i4] := bArrDataIn[i][i4] shl 4;
                    bArrDataIn[i][i4] := bArrDataIn[i][i4] AND $F0;
                    if(sTemp[i3+1] <= '9') then
                        bArrDataIn[i][i4] := bArrDataIn[i][i4] + (byte(sTemp[i3+1]) - $30)
                    else
                        bArrDataIn[i][i4] := bArrDataIn[i][i4] + (byte(sTemp[i3+1]) - $37);

                    i3 := i3 + 2;
                    i4 := i4 + 1;
                end;
                i3 := 1;
                i4 := 0;
                while (i3 < length(sTemp2)) do
                Begin
                    //Convert Output Data
                    if(sTemp2[i3] <= '9') then
                        bArrDataOut[i][i4] := (byte(sTemp2[i3]) - $30)
                    else
                        bArrDataOut[i][i4] := byte(sTemp2[i3]) - $37;

                    bArrDataOut[i][i4] := bArrDataOut[i][i4] shl 4;
                    bArrDataOut[i][i4] := bArrDataOut[i][i4] AND $F0;
                    if(sTemp2[i3+1] <= '9') then
                        bArrDataOut[i][i4] := bArrDataOut[i][i4] + (byte(sTemp2[i3+1]) - $30)
                    else
                        bArrDataOut[i][i4] := bArrDataOut[i][i4] + (byte(sTemp2[i3+1]) - $37);

                    i3 := i3 + 2;
                    i4 := i4 + 1;
                End;
                bCountDataOut[i] := i4;
                //showmessage(inttohex(bArrDataOut[i][0],2)+inttohex(bArrDataOut[i][1],2));
            end;
        end;
        i:=i+1;
        iGNumOfDataIn := i;
    end;
    until sData[i-1] = 'kosong';
  finally
    SettingsFile.Free;
  end;
end;


procedure TForm1.Button1Click(Sender: TObject);
var bRes: Boolean;
begin
    OpenIniFile;
    case iBaudrate of
        115200 : ComPort1.BaudRate := br115200;
        9600 : ComPort1.BaudRate := br9600;
    end;
    ComPort1.Port := 'COM'+inttostr(iPort);
    Comport1.Connected := true;
    if Comport1.Connected = false then
        showmessage('Gagal open comport')
    else
    begin
        Button1.Enabled := False;
        Button2.Enabled := True;
        Timer1.Enabled := True;
        Timer1.Enabled := true;
        iGbCounterBuff := 0;
    end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
    Timer1.Enabled := False;
    Button1.Enabled := True;
    Button2.Enabled := False;
    ComPort1.Connected := False;
    bSerStatus := 0;
    iGbCounterBuff := 0;
    iGbLengthOfEvent := 0;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
    pwIniFile : pwideChar ;
    sIniFile : String;
begin
    sIniFile := ChangeFileExt(Application.ExeName, '.ini');
    pwIniFile := addr(sIniFile[1]);
    ShellExecute(Handle,'open', 'c:\windows\notepad.exe',pwIniFile, nil, SW_SHOWNORMAL) ;
end;
procedure TForm1.Button4Click(Sender: TObject);
var
    bData : array [0..15] of byte;
begin

    bData[0] := $08;
    bData[1] := $04;
    bData[2] := $04;
    bData[3] := $0A;
    bData[4] := $78;
    bData[5] := $85;
    bData[6] := $8D;
    bData[7] := $01;
    bData[8] := $CC;
    bData[9] := $28;
    bData[10] := $0C;
    bData[11] := $02;
    bData[12] := $5A;
    bData[13] := $01;
    comport1.write(bData,14);
end;
Procedure TForm1.fCheckCommand();
var
    i  : integer;
    i2 : integer;
begin
    i2 := 0;
    while i2 < iGNumOfDataIn do
    begin
        i := 0;
        while True do
        begin
            //showmessage(inttohex(bArrDataIn[i2][i],2)+'?'+inttohex(bActiveSerialBuff[i],2)+'-'+inttostr(iActiveSerialBuffLen)+'-'+inttostr(i));
            if (bArrDataIn[i2][i] <> bActiveSerialBuff[i])
                OR (bArrDataIn[i2][i] = 0) OR (bActiveSerialBuff[i] = 0) then
                begin
                    Break;
                end
            else if (i = iGbCounterBuff) then
            begin
                //showmessage('send');
                ComPort1.Write(bArrDataOut[i2],bCountDataOut[i2]);
                listbox1.AddItem('<OUT>'+ConvertByteToStr(bArrDataOut[i2],bCountDataOut[i2]-1),nil);
                Exit;
            end;
            i := i + 1;
        end;
        i2 := i2 + 1;
    end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
    bArrDataInputSerial: array[0..80] of byte;
    sArrActiveBuf: String;
    BytesRead: integer;
    cTemp : Integer;
    iRes : integer;
begin
    Timer1.Enabled := false;
    BytesRead := comport1.Read(bArrDataInputSerial,length(bArrDataInputSerial));
    //showmessage('Read');
    if BytesRead > 0 then
    begin
        if((iGbCounterBuff + BytesRead) >= 100) then
        begin
            ShowMessage('Buffer serial overflow, resetting counter');
            iGbCounterBuff := 0;
            Exit;
        end;
        //showmessage('read serial : '+inttostr(bytesread));
        Move(bArrDataInputSerial[0], bActiveSerialBuff[iGbCounterBuff], BytesRead);
        iActiveSerialBuffLen := iGbCounterBuff + BytesRead;
        //showmessage(inttostr(iActiveSerialBuffLen));
        while iGbCounterBuff < iActiveSerialBuffLen do
        begin
            //showmessage(inttostr(iGbCounterBuff) + '-'+ inttohex(bActiveSerialBuff[iGbCounterBuff],2) + '-' + inttostr(iActiveSerialBuffLen)+'-'+inttostr(iGbLengthOfEvent + iGCountBase) + 'ber'+inttostr(bSerStatus));
            case iMode of
                1 : //Bluetooth mode
                begin
                    if bSerStatus = 0 then //Waiting for HCI Header
                    begin
                        if ((bActiveSerialBuff[iGbCounterBuff] = $4)
                            OR (bActiveSerialBuff[iGbCounterBuff] = $2)) Then
                        begin
                            //ShowMessage('0');
                            iActiveSerialBuffLen := iActiveSerialBuffLen - iGbCounterBuff;
                            Move(bActiveSerialBuff[iGbCounterBuff], bActiveSerialBuff[0], iActiveSerialBuffLen);
                            iGbCounterBuff := 0;
                            bSerStatus := 1;
                            //Showmessage(inttostr(bArrDataInputSerial[0])); //debug
                        end;
                    end;
                    if bSerStatus = 1 then //Waiting and reading length
                    begin
                         if((bActiveSerialBuff[0] = $4) AND (iGbCounterBuff = 2)) Then
                         begin
                            iGbLengthOfEvent := bActiveSerialBuff[iGbCounterBuff];
                            bSerStatus := 2;
                            iGCountBase := 3 - 1;
                         end;
                         if((bActiveSerialBuff[0] = $2) AND (iGbCounterBuff = 4)) Then
                         begin
                            iGbLengthOfEvent := bActiveSerialBuff[iGbCounterBuff];
                            bSerStatus := 2;
                            iGCountBase := 5 - 1;
                         end;
                    end;
                    if bSerStatus = 2 then //Waiting last data to check command
                    begin
                        //ShowMessage(inttostr(iGbLengthOfEvent + iGCountBase) + 'X' + inttostr(iGbCounterBuff));
                        if (iGbCounterBuff = (iGbLengthOfEvent + iGCountBase)) then
                        begin
                            listbox1.AddItem('<IN>'+ConvertByteToStr(bActiveSerialBuff,iGbCounterBuff),nil);
                            fCheckCommand(); //Compare byte and execute command
                            //showmessage('gak sama' + inttostr(bArrDataIn[3][0]) + 'H' + inttostr(bArrDataIn[3][1]));
                            iActiveSerialBuffLen := iActiveSerialBuffLen - iGbCounterBuff - 1;
                            //showmessage('num'+inttostr(iActiveSerialBuffLen)+'num');
                            if(iActiveSerialBuffLen > 0) then
                                Move(bActiveSerialBuff[iGbCounterBuff+1], bActiveSerialBuff[0], iActiveSerialBuffLen);

                            //ShowMessage(inttostr(bActiveSerialBuff[0])+'X'+inttostr(iActiveSerialBuffLen)+ 'countbuff' + inttostr(iGbCounterBuff));
                            bSerStatus := 0;
                            iGbCounterBuff := -1;
                        end;
                    end;
                end;
            end;
            iGbCounterBuff := iGbCounterBuff + 1;
        end;
        {

        }
    end
    else
    begin
        { Raise an exception }
        //showmessage('Tidak ada data');
    end;
    Timer1.Enabled := true;
end;

end.
